package com.esdk.utils;

import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.esdk.esdk;
import com.esdk.exception.SdkRuntimeException;
import lombok.NonNull;

import java.io.*;
import java.lang.Character.UnicodeBlock;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.esdk.utils.Constant.CRLF;
import static com.esdk.utils.Constant.UTF8;

/***
 * @author 范飞宇
 * @since 2003.?.?
 */
public class EasyStr{
  final public static DecimalFormat df=new DecimalFormat("#,###");
  final public static String DefaultSeperator=",";
  /**
   * 处理中文数字的排序问题
   */
  private final static String sortRegex="零|一|二|三|四|五|六|七|八|九|十|上|中|下";
  private static String PREFIX="\\u";

  static{
    df.setMaximumFractionDigits(2);
  }

  public static String getFileExtName(@NonNull String filename){ // 注意:后缀名可能不止3个字节
    return esdk.file.getFileExtName(filename);
  }

  public static String getClassShortName(@NonNull String className){
    return className.substring(className.lastIndexOf('.')+1);
  }

  public static void InputStreamToOutputStream(@NonNull InputStream is,@NonNull OutputStream os) throws IOException{
    int bufferSize=1024;
    byte buff[]=new byte[bufferSize];
    int readSize;
    while((readSize=is.read(buff))!=-1){
      os.write(buff,0,readSize);
    }
    is.close();
    os.close();
  }

  public static String isToStr(@NonNull InputStream is){
    return readerToStr(new InputStreamReader(is),1024);
  }

  public static String isToStr(@NonNull InputStream is,boolean isAutoClose){
    return readerToStr(new InputStreamReader(is),1024,isAutoClose);
  }

  public static String isToStr(@NonNull InputStream is,String charset) throws UnsupportedEncodingException{
    InputStreamReader isr=charset==null?new InputStreamReader(is):new InputStreamReader(is,charset);
    return readerToStr(isr,1024);
  }

  public static InputStream strToIs(@NonNull String value){
    // StringBufferInputStream is=new StringBufferInputStream(Value);
    ByteArrayInputStream is=new ByteArrayInputStream(value.getBytes());
    return is;
  }

  public static InputStreamReader strToIsr(@NonNull String value,String charset) throws Exception{
    InputStream is=new ByteArrayInputStream(value.getBytes());
    InputStreamReader isr=new InputStreamReader(is,charset);// gb2312
    // ByteArrayInputStream isb=new ByteArrayInputStream(Value.getBytes());
    return isr;
  }

  public static InputStreamReader strToIsr(@NonNull String value) throws Exception{
    InputStream is=new ByteArrayInputStream(value.getBytes());
    InputStreamReader isr=new InputStreamReader(is,"GB2312");// gb2312
    // ByteArrayInputStream isb=new ByteArrayInputStream(Value.getBytes());
    return isr;
  }

  public static String readerToStr(@NonNull Reader isr){
    return readerToStr(isr,4096);
  }

  public static String readerToStr(@NonNull Reader isr,int BufferSize){
    return readerToStr(isr,BufferSize,true);
  }

  public static String readerToStr(@NonNull Reader isr,int BufferSize,boolean isCloseReader){
    StringBuffer result=new StringBuffer();
    char[] buf=new char[BufferSize];
    int readlen=0;
    try{
      while((readlen=isr.read(buf))>0){
        result=result.append(buf,0,readlen);
        /*
         * if(readlen<BufferSize) break;
         */
      }
      if(isCloseReader)
        isr.close();
    }catch(Exception e){
      throw esdk.tool.wrapThrowble(e);
    }
    return result.toString();
  }

  public static String getFileNameNotExt(@NonNull String filename){
    for(int i=filename.length()-1;i>=0;i--){
      if(filename.substring(i,i+1).equals(".")){
        return filename.substring(0,i);
      }
    }
    return null;
  }

  public static String subString4BytePos(@NonNull String value,int start){
    return getString4ByteLength(value,start,value.length()-start);
  }

  public static String subString4BytePos(@NonNull String value,int start,int end){
    return getString4ByteLength(value,start,end-start);
  }

  public static String ellipsis4Byte(@NonNull String s,int maxByteLen){
    if(s.getBytes().length>maxByteLen)
      s=getSubASCIILength(s,0,maxByteLen-3)+"...";
    return s;
  }


  public static String ellipsis4ByteUTF8(@NonNull String str,int maxByteLen){
    return ellipsis4Byte(str,maxByteLen,UTF8);
  }

  public static String ellipsis(String str,int len){
    if(str==null||len<0) return null;
    if(len==0) return "";
    String ellipsisStr="...";
    if(len<ellipsisStr.length()&&str.length()>len) return str.substring(0,len);
    if(str.length()>=len-ellipsisStr.length()) return str.substring(0,len-ellipsisStr.length()).concat(ellipsisStr);
    return str;
  }

  public static String ellipsis4Byte(@NonNull String s,int maxByteLen,String charset){
    byte[] src=null;
    try{
      src=s.getBytes(charset);
      if(src.length>maxByteLen){
        int newlen=src[maxByteLen-1]<0?maxByteLen-3:maxByteLen-4;
        String s1=new String(Arrays.copyOf(src,newlen),charset);
        if((int)s1.charAt(s1.length()-1)==65533)
          s1=s1.substring(0,s1.length()-1);
        return s1+"...";
      }else
        return s;
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static String getString4ByteLength(@NonNull String s,int start,int len){
    String result="";
    byte b[]=s.getBytes();
    byte v[]=new byte[len];
    if(b.length<=len)
      result=s;
    else if(b.length-1>=start&&b.length>=start+len){
      for(int i=start, n=len+start;i<n;i++){
        v[i-start]=b[i];
      }
      result=new String(v);
    }
    return result;
  }

  public static String getSubASCIILength(@NonNull String s,int start,int len){
    String result=getString4ByteLength(s,start,len);
    boolean iscut=result.length()>0&&(int)result.charAt(result.length()-1)==65533;
    return iscut?result.substring(0,result.length()-1):result;
  }

  public static String[] getStringsByLength(@NonNull String s,int len){
    char[] cArray=s.toCharArray();
    byte[] bArray=s.getBytes();
    ArrayList<String> result=new ArrayList<String>(s.length()/len+1);
    int start=0, end=start;
    for(int i=0, n=cArray.length;i<n;i++){
      end+=isChinese(cArray[i])?2:1;
      if(end-start==len){ // it may be 69 or 70
        result.add(new String(bArray,start,end-start));
        start=end;
      }else if(end-start==len+1){
        result.add(new String(bArray,start,end-start-2));
        start=end-2;
      }
    }
    if(start<end)
      result.add(new String(bArray,start,end-start));
    return result.toArray(new String[0]);
  }

  public static String urlDecode(@NonNull String value){
    try{
      return URLDecoder.decode(value,Constant.UTF8);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static String urlEncode(@NonNull String value){
    try{
      return URLEncoder.encode(value,Constant.UTF8);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static String intToString(int value,int outputLength){
    if(value<0){
      return "";
    }
    String result=fillchar('0',outputLength);
    String strIntValue=String.valueOf(value);
    if(strIntValue.length()>=outputLength)
      result=strIntValue;
    else
      result=result.substring(0,outputLength-strIntValue.length())+strIntValue;
    return result;
  }

  public static String doubleToString(double value,int outputLength){
    if(value<0){
      return "";
    }
    String result=fillchar('0',outputLength);
    String NumberValue=value==(int)value?String.valueOf((int)value):String.valueOf(value);
    if(NumberValue.length()>=outputLength)
      result=NumberValue;
    else
      result=result.substring(0,outputLength-NumberValue.length())+NumberValue;
    return result;
  }

  public static String bigDecimalToString(BigDecimal value,int outputLength,int digit){
    String result=null;
    double d=round(value.doubleValue(),digit);
    if(d==(int)d)
      result=intToString(value.intValue(),outputLength);
    else
      result=doubleToString(d,outputLength);
    return result;
  }

  public static String fillchar(char sign,int len){
    len=len>0?len:0;
    char[] result=new char[len];
    for(int i=0;i<len;i++){
      result[i]=sign;
    }
    return new String(result);
  }

  private static String[][] getStringArray2(@NonNull List<String[][]> al){
    StringBuffer sb=new StringBuffer();
    for(int i=0;i<al.size();i++){
      String[][] value=al.get(i);
      if(i<al.size())
        sb.append(arrToCsv(value,",")+CRLF);
      else
        sb.append(arrToCsv(value,","));
    }
    return csvToArr2(sb.toString(),",");
  }

  public static int compareTo(String s1,String s2){
    int result=0;
    if(s1==s2)
      result=0;
    else if(s1==null&&s2!=null)
      result=-1;
    else if(s1!=null&&s2==null)
      result=1;
    else if(s1.equals(s2))
      return 0;
    if(s1.matches(".*(\\d+).*"))
      s1=replaceSerialNo(s1);
    if(s2.matches(".*(\\d+).*"))
      s2=replaceSerialNo(s2);
    if(s1!=null&&esdk.regex.find(s1,sortRegex)){
      s1=replaceChineseNum(s1);
    }
    if(s2!=null&&esdk.regex.find(s2,sortRegex)){
      s2=replaceChineseNum(s2);
    }
    result=s1.compareTo(s2);
    return result;
  }

  /**
   * 暂时只能解决100之内的汉字数字
   */
  public static String replaceChineseNum(@NonNull String s){
    String result="";
    result=esdk.regex.replaceAll(s,"([零一二三四五六七八九十上中下]+)",m->chineseNumber2Number(m.group(0)));
    return result;
  }

  /**
   * 暂时只能解决100之内的汉字数字
   */
  private static String replaceSerialNo(@NonNull String s){
    String result="";
    try{
      result=esdk.regex.replaceAll(s,"(\\d+)",m->insertZero(m.group(1),4));
    }catch(Exception e){
      e.printStackTrace();
    }
    return result;
  }

  private static String chineseNumber2Number(@NonNull String s){
    if(s.length()==1){
      s=s.replaceAll("十","10");
    }else if(s.startsWith("十")){
      s=s.replaceAll("十","1");
    }else if(s.endsWith("十")){
      s=s.replaceAll("十","0");
    }else if(s.length()>2)
      s=s.replaceAll("十","");
    s=s.replaceAll("零","0").replaceAll("一","1").replaceAll("二","2").replaceAll("三","3").replaceAll("四","4")
      .replaceAll("五","5").replaceAll("六","6").replaceAll("七","7").replaceAll("八","8").replaceAll("九","9")
      .replaceAll("上","001").replaceAll("中","002").replaceAll("下","003");
    return intToString(esdk.math.toInt(s),3);
  }

  public static boolean existOf(@NonNull String s,@NonNull String findkey,boolean isIgnoreCase){
    if(findSubString(s,findkey,isIgnoreCase)>=0){
      return true;
    }
    return false;
  }

  public static boolean existOf(String s,String findkey){
    if(s==null||findkey==null)
      return false;
    if(findSubString(s,findkey,true)>=0){
      return true;
    }
    return false;
  }

  public static boolean existAnd(String s,String[] findkeys,boolean isIgnoreCase){
    if(s==null||findkeys==null||findkeys.length==0)
      return false;
    for(int i=0;i<findkeys.length;i++){
      if(findSubString(s,findkeys[i],isIgnoreCase)<0){
        return false;
      }
    }
    return true;
  }

  public static boolean existOf(String[] array,String find,boolean isIgnoreCase){
    if(EasyObj.isValid(array)&&EasyObj.isValid(find)){
      for(int i=0;i<array.length;i++){
        if(equals(array[i],find,isIgnoreCase))
          return true;
      }
    }
    return false;
  }

  public static boolean existAnd(String s,String[] findkeys){
    return existAnd(s,findkeys,false);
  }

  public static boolean existOr(String s,String[] findkeys){
    return existOr(s,findkeys,false);
  }

  public static boolean existOr(String s,String[] findkeys,boolean isIgnoreCase){
    for(int i=0;i<findkeys.length;i++){
      if(findSubString(s,findkeys[i],isIgnoreCase)>=0)
        return true;
    }
    return false;
  }

  public static boolean contains(String s,String... keywords){
    boolean result=true;
    if(s!=null){
      for(String item: keywords){
        result=result&&s.contains(item);
        if(!result)
          break;
      }
    }
    return result;
  }

  public static boolean containsOr(String str,String... findwords){
    boolean result=false;
    if(str!=null){
      for(String item: findwords){
        result=result||str.contains(item);
        if(result)
          break;
      }
    }
    return result;
  }

  public static int indexOf(@NonNull String str,String findword,int start){
    int result=0;
    if(start<0){
      start=0;
    }
    if(str.length()<start+1){
      return -1;
    }
    str=str.substring(start,str.length()).toLowerCase();
    result=str.indexOf(findword.toLowerCase());
    if(result>=0){
      result=start+result;
      return result;
    }else{
      return -1;
    }
  }

  public static String getAppPath(){ // 得到程序目录
    return System.getProperty("user.dir");
  }

  public static String XmlStringFilter(@NonNull String xml){
    if(xml.indexOf("&amp;")>=0){
      xml=replaceAll(xml,"&amp;","＆",false); // 避免&字符串的错误
    }else if(xml.indexOf("&#38;#38;")>=0){
      xml=replaceAll(xml,"&#38;#38;","＆",false); // 避免&字符串的错误
    }else if(xml.indexOf("&x#26;x#26")>=0){
      xml=replaceAll(xml,"&x#26;x#26","＆",false); // 避免&字符串的错误
    }else if(xml.indexOf("&")>=0){
      xml=replaceAll(xml,"&","＆",false); // 避免&字符串的错误
    }
    return xml;
  }

  public static String setXmlNodeValue(@NonNull String xmlString,@NonNull String NodeName,String value){
    String startnodename="<"+NodeName+">";
    String endnodename="</"+NodeName+">";
    int i=indexOf(xmlString,startnodename,0)+startnodename.length();
    int j=indexOf(xmlString,endnodename,i);
    if((i<j)&&(j>=0)){
      xmlString=xmlString.substring(0,i)+value+xmlString.substring(j);
    }
    return xmlString;
  }

  public static String getDirPath(String value){
    return esdk.file.getDirPath(value);
  }

  public static double round(double value,int holdDigit){
    double a=java.lang.Math.pow(10,Double.valueOf(String.valueOf(holdDigit)).doubleValue());
    return (Long.valueOf(java.lang.Math.round(value*a))).doubleValue()/a;
  }

  public static String[][] getStringArray(@NonNull List list){ // 二维链表转换成二维字符串数组
    int one=0, two=0;
    one=list.size();
    if(one<=0){
      return new String[0][0];
    }
    two=((List)list.get(0)).size();
    String[][] result=new String[one][two];
    for(int i=0;i<one;i++){
      List o=(List)list.get(i);
      result[i]=(String[])o.toArray(new String[0]);
    }
    return result;
  }

  /**
   * two-dimensional array to list<list>
   */
  public static List<List> arrArrToListList(@NonNull String[][] arr){
    ArrayList result=new ArrayList(arr.length);
    for(int i=0, n=arr.length;i<n;i++){
      ArrayList sub=new ArrayList(arr[i].length);
      for(int j=0;j<arr[i].length;j++){
        sub.add(arr[i][j]);
      }
      result.add(sub);
    }
    return result;
  }

  /**
   * list<list> => String[][]
   */
  public static String[][] listToArr2(@NonNull List<List> al){
    if(al.isEmpty()) return new String[0][];
    int arrlen=al.get(0).size();
    String[][] result=new String[al.size()][arrlen];
    for(int i=0;i<result.length;i++){
      int checklen=al.get(i).size();
      if(checklen!=arrlen)
        throw new SdkRuntimeException("第"+i+"行字段数与默认不相等");
      System.arraycopy(al.get(i).toArray(),0,result[i],0,checklen);
    }
    return result;
  }

  /**
   * Object[][] => String[][]
   */
  public static String[][] toArray2(@NonNull Object[][] al){
    if(al.length==0) return new String[0][];
    int arrlen=al[0].length;
    String[][] result=new String[al.length][arrlen];
    for(int i=0;i<result.length;i++){
      int checklen=al[i].length;
      if(checklen!=arrlen)
        throw new SdkRuntimeException("第"+i+"行字段数与默认不相等");
      result[i]=new String[checklen];
      for(int j=0;j<checklen;j++){
        result[i][j]=valueOf(al[i][j]);
      }
    }
    return result;
  }

  public static int findSubString(String s,String sub,boolean isIgnoreCase){
    return indexOf(s,sub,isIgnoreCase);
  }

  public static int indexOf(@NonNull String str,@NonNull String subs,boolean isIgnoreCase){
    int pos=-1;
    if(isIgnoreCase){
      str=str.toLowerCase();
      subs=subs.toLowerCase();
      pos=str.indexOf(subs);
    }else{
      pos=str.indexOf(subs);
    }
    return pos;
  }

  public static String getSubString(String str,int start,int end){
    String result=null;
    if(str!=null&&start<end&&str.length()>0){
      if(start<str.length())
        if(end>str.length())
          end=str.length();
      result=str.substring(start,end);
    }
    return result;
  }

  public static String getXmlNodeValue(@NonNull String xmlString,@NonNull String NodeName){
    String strresult="";
    String startnodename="<"+NodeName+">";
    String endnodename="</"+NodeName+">";
    int i=indexOf(xmlString,startnodename,0)+startnodename.length();
    int j=indexOf(xmlString,endnodename,i);
    if((i<j)&&(j>=0)){
      strresult=xmlString.substring(i,j);
    }
    return strresult;
  }

  public static String getXmlNodeValueAll(@NonNull String xmlString,@NonNull String NodeName,@NonNull String splitSign){
    StringBuffer result=new StringBuffer();
    int i=0;
    while(i>=0){
      i=getXmlNodeValueAll(xmlString,NodeName,i,result,splitSign);
    }
    return result.toString();
  }

  private static int getXmlNodeValueAll(@NonNull String xmlString,@NonNull String NodeName,int position,@NonNull StringBuffer result,@NonNull String splitSign){
    String startnodename="<"+NodeName+">";
    String endnodename="</"+NodeName+">";
    if(indexOf(xmlString,startnodename,position)<0)
      return -1;
    int i=indexOf(xmlString,startnodename,position)+startnodename.length();
    int j=indexOf(xmlString,endnodename,i);
    if((i<j)&&(j>=0)){
      result.append(xmlString.substring(i,j)+splitSign);
    }
    return j;
  }

  // <hongyuanjiao>
  public static @NonNull String numericToDateFormat(@NonNull String numericStr){
    if(numericStr.length()<14||numericStr.length()>14)
      return numericStr;// 如果不足14位，例如0，就直接返回，以免出错
    String dateTimeStr=numericStr.substring(0,4)+"-"+numericStr.substring(4,6)+"-"+numericStr.substring(6,8)+" "
      +numericStr.substring(8,10)+":"+numericStr.substring(10,12)+":"+numericStr.substring(12,14);
    return dateTimeStr;
  }

  public static String dateFormatToNumeric(String dateTimeStr){
    String numericStr=dateTimeStr.substring(0,4)+dateTimeStr.substring(5,7)+dateTimeStr.substring(8,10)
      +dateTimeStr.substring(11,13)+dateTimeStr.substring(14,16)+dateTimeStr.substring(17,19);
    return numericStr;
  }

  public static String convertString(String chinastring){ // 解决中文乱码问题
    if(chinastring==null){
      return null;
    }
    String result=chinastring;
    try{
      byte[] b=chinastring.getBytes("iso-8859-1");
      byte[] b1=chinastring.getBytes();
      if(b.length>0&&b1.length==b.length){
        result=new String(b);
      }
      return result;
    }catch(UnsupportedEncodingException ex){
      throw new SdkRuntimeException("not support iso-8859-1");
    }
  }

  public static @NonNull String encodeToBase64(@NonNull String str){
    return Base64.getEncoder().encodeToString(str.getBytes());
  }

  public static @NonNull String decodeFromBase64(@NonNull String str){
    return new String(Base64.getDecoder().decode(str));
  }

  public static @NonNull String encodeUrl(@NonNull String str){
    return URLEncodeUtil.encode(str);
  }

  public static @NonNull String decodeUrl(@NonNull String str){
    return URLDecoder.decode(str);
  }

  /**
   * base64编码
   */
  public static @NonNull String encode(@NonNull String str){
    return Base64.getEncoder().encodeToString(str.getBytes());
  }

  /**
   * base64解码
   */
  public static String decode(String value){
    return new String(Base64.getDecoder().decode(value));
  }

  public static String toCsv(List list){
    return EasyCsv.toCsv(listToArr2(list));
  }

  public static String toCsv(String[][] csv){
    return EasyCsv.toCsv(csv);
  }

  public static String toCsv(String[][] csv,boolean forceText,boolean isTrim){
    return EasyCsv.toCsv(csv,forceText,isTrim);
  }

  public static String arrToCsv(String[][] strArray,String delimit){
    return EasyCsv.toCsv(strArray,delimit,CRLF,false);
  }

  public static String arrToCsv(String[][] strArray,String delimit,String endlinesign){
    return EasyCsv.toCsv(strArray,delimit,endlinesign,false);
  }

  public static String[][] fromCsv(String csv){
    return EasyCsv.fromCsv(csv);
  }

  public static String[][] fromCsv(String csv,boolean forceText){
    return EasyCsv.fromCsv(csv,forceText);
  }

  public static String[][] fromCsv(String csv,String delimit){
    return EasyCsv.fromCsv(csv,delimit);
  }

  @Deprecated
  /** replace to fromCsv **/
  public static String[][] csvToArr2(String csvString,String delimit,boolean isCheck){
    return csvToArr2(csvString,delimit,Constant.CR$LF,isCheck,false,true);
  }

  @Deprecated
  /** replace to fromCsv **/
  public static String[][] csvToArr2(String csv,String delimit,boolean isCheck,boolean delmark){
    return csvToArr2(csv,delimit,Constant.CR$LF,isCheck,false,delmark);
  }

  @Deprecated
  /** replace to fromCsv **/
  public static String[][] csvToArr2(String csvString,String delimit){
    return csvToArr2(csvString,delimit,Constant.CR$LF,true,false,true);
  }

  @Deprecated
  /** replace to fromCsv **/
  public static String[][] csvToArr2(String csvString,String delimit,String endlinesign){
    return csvToArr2(csvString,delimit,endlinesign,true,false,true);
  }

  @Deprecated
  /** replace to fromCsv **/
  public static String[][] csvToArr2(String csvString,String delimit,String endlinesign,boolean isCheck,boolean isTrim){
    return csvToArr2(csvString,delimit,endlinesign,isCheck,isTrim,true);
  }

  @Deprecated
  /** replace to fromCsv **/
  public static String[][] csvToArr2(String csvString,String delimit,String endlinesign,boolean isCheck,boolean isTrim,boolean isDelMark){
    if(csvString==null||csvString.length()==0){
      return new String[0][0];
    }
    String value="";
    int arrlen=-1;
    ArrayList al=new ArrayList();
    if(csvString.indexOf(CRLF)>=0&&endlinesign.equals("\n"))
      endlinesign=CRLF;
    else if(csvString.indexOf(CRLF)<0&&endlinesign.equals(CRLF))
      endlinesign="\n";
    StringTokenizer temp=new StringTokenizer(csvString,endlinesign);
    int indexline=0;
    while(temp.hasMoreElements()){
      List arline=new ArrayList(temp.countTokens());
      value=temp.nextToken();
      arline=Arrays.asList(smartStrToArr(value,delimit,isTrim,isDelMark));
      if(arrlen==-1){
        arrlen=arline.size();
      }else{
        if(arrlen!=arline.size()&&isCheck){
          throw new SdkRuntimeException("第"+indexline+"行字段数与默认不相等");
        }
      }
      al.add(arline);
      indexline+=1;
    }
    String[][] result=getStringArray(al);
    return result;
  }

  public static ArrayList arrToList(String[] value){
    try{
      return arrToList(value,0,value.length,true);
    }catch(Exception ex){
      return null;
    }
  }

  public static ArrayList arrToList(String[] arr,int startPos,int len,boolean isCheck){
    if(isCheck){
      if(len<0){
        throw new SdkRuntimeException("长度不可以为负值");
      }
      if(arr.length<startPos+len){
        throw new SdkRuntimeException("复制的长度超过数组长度");
      }
    }
    ArrayList result=new ArrayList(len);
    for(int i=startPos;i<startPos+len;i++){
      result.add(arr[i]);
    }
    return result;
  }

  public static String arrToStr(String[] arr){
    return arrToStr(arr,",");
  }

  /**
   * now throw Exception,because array's len impossible occur incorrect.
   *
   * @param arr
   * @param breaksign
   * @return
   */
  public static String arrToStr(String[] arr,String breaksign){
    if(arr==null)
      return null;
    try{
      return arrToStr(arr,breaksign,arr.length);
    }catch(Exception e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static @NonNull String arrToStr(@NonNull String[] arr,@NonNull String breaksign,int length){
    if(length>arr.length){
      throw new SdkRuntimeException("数组长度有误");
    }
    StringBuffer result=new StringBuffer("");
    for(int i=0;i<length;i++){
      if(arr[i]==null)
        arr[i]="";
      if(arr[i].indexOf(breaksign)>=0)
        result.append("\"").append(arr[i]).append("\"");
      else
        result.append(arr[i]);
      if(i<length-1)
        result.append(breaksign);
    }
    return result.toString();
  }

  public static String listToStr(List list,String breaksign){
    List strlist=new ArrayList(list.size());
    for(Object item:list){
      strlist.add(esdk.str.valueOf(item));
    }
    return String.join(breaksign,strlist);
  }

  public static String listToStr(List list){
    CharAppender result=new CharAppender(',');
    return result.append(list).toString();
  }

  public static String[] toStringArray(Object[] array){
    if(array==null)
      return null;
    String[] result=new String[array.length];
    for(int i=0;i<result.length;i++){
      if(array[i]!=null){
        result[i]=valueOf(array[i]);
      }
    }
    return result;
  }

  public static String[] toArray(Collection collection){
    if(collection==null)
      return null;
    int i=0;
    String[] result=new String[collection.size()];
    for(Object obj: collection){
      if(obj!=null)
        result[i++]=valueOf(obj);
    }
    return result;
  }

  public static String[][] csvToArr2(@NonNull String value,int[] sa,boolean isTrim){
    String[][] result=new String[0][];
    if(value.length()==0)
      return result;
    else{
      value=value.replaceAll(CRLF,"\n");
      StringTokenizer st=new StringTokenizer(value,"\n");
      if(st.hasMoreElements()){
        result=new String[st.countTokens()][];
        int i=0;
        while(st.hasMoreElements()){
          result[i]=strToArr(st.nextToken(),sa,isTrim);
          i+=1;
        }
      }
      return result;
    }
  }

  public static String[] strToArr(String value,int[] sa,boolean isTrim){
    ArrayList al=new ArrayList(sa.length);
    for(int i=0;i<sa.length-1;i++){// 记录每个字段的起始位置,包括结尾位置
      if(value.getBytes().length<sa[i+1])
        throw new SdkRuntimeException("定义长度超出字串长度");
      String temp=getString4ByteLength(value,sa[i],sa[i+1]-sa[i]);
      if(isTrim)
        temp=temp.trim();
      al.add(temp);
    }
    String[] result=(String[])al.toArray(new String[0]);
    return result;
  }

  public static String[] strToArr(String value){
    return strToArr(value,",");
  }

  public static String[] strToArr(String value,String breaksign){
    return strToArr(value,breaksign,false);
  }

  public static String[] strToArr(String value,String breaksign,boolean isTrim){
    return strToArr(value,breaksign,isTrim,false);
  }

  public static String[] strToArr(String value,String breaksign,boolean isTrim,boolean isdelmark){
    if(value==null||value.length()==0){
      return new String[0];
    }
    int ipos=0;
    ArrayList al=new ArrayList();
    String temp;
    while(ipos>=0){
      ipos=value.indexOf(breaksign);
      if(ipos>=0){
        temp=value.substring(0,ipos);
        value=value.substring(ipos+breaksign.length());
      }else{
        temp=value;
      }
      if(isTrim&&temp.indexOf(" ")>=0)
        temp=temp.trim();
      if(isdelmark)
        if(temp.indexOf("\"")==0&&temp.lastIndexOf("\"")==temp.length()-1&&temp.length()>1)
          temp=temp.substring(1,temp.length()-1);
      al.add(temp);
    }
    String[] result=(String[])al.toArray(new String[0]);
    return result;
  }

  public static String[] smartStrToArr(@NonNull String value,@NonNull String breaksign,boolean isTrim,boolean isdelmark){
    return smartStrToArr(value,breaksign,"\"",isTrim,isdelmark);
  }

  public static String[] smartStrToArr(@NonNull String value,@NonNull String breaksign,String qualifier,boolean isTrim,boolean isdelmark){
    if(value==null||value.length()==0){
      return new String[0];
    }
    int ipos=0;
    ArrayList al=new ArrayList();
    while(ipos>=0){
      ipos=value.indexOf(breaksign);
      String temp=null;
      if(ipos>=0){
        temp=value.substring(0,ipos);
        if(temp.indexOf(qualifier)==0&&temp.lastIndexOf(qualifier)==temp.length()-1&&temp.length()>1){
          if(isdelmark)
            temp=temp.substring(1,temp.length()-1);
        }
        if((temp.indexOf(qualifier)==0&&temp.lastIndexOf(qualifier)!=temp.length()-1)
          ||(temp.indexOf(qualifier)==0&&temp.length()==1)){
          int ipostail=value.indexOf(qualifier,ipos+1);
          int iposnext=value.indexOf(breaksign,ipostail+1);
          if(iposnext>ipostail&&ipostail>=0){
            if(isdelmark)
              temp=value.substring(1,ipostail);
            else
              temp=value.substring(0,ipostail+1);
            value=value.substring(iposnext+breaksign.length());
          }else if(iposnext<0){
            if(isdelmark)
              temp=value.substring(1,ipostail);
            else
              temp=value.substring(0,ipostail+1);
            value=value.substring(ipostail+breaksign.length());
            ipos=-1;
          }else if(ipostail<0){
            temp=value;
            ipos=-1;
          }
        }else{
          if(ipos+1<=value.length())
            value=value.substring(ipos+breaksign.length());
        }
      }else{
        if(isdelmark&&value.indexOf(qualifier)==0&&value.lastIndexOf(qualifier)==value.length()-1&&value.length()>1)
          temp=value.substring(1,value.length()-1);
        else
          temp=value;
      }
      if(isTrim&&temp.indexOf(" ")>=0)
        temp=temp.trim();
      al.add(temp);
    }
    String[] result=(String[])al.toArray(new String[0]);
    return result;
  }

  public static String convertToCRLF(@NonNull String value){
    if(value.indexOf(CRLF)>=0)
      return value;
    return value.replaceAll("\n",CRLF);
  }

  public static int getSubStringCount(@NonNull String str,@NonNull String substring){
    int result=0;
    int pos=1;
    while((pos=str.indexOf(substring))>=0){
      result++;
      if(pos>=0)
        str=str.substring(pos+1,str.length());
    }
    return result;
  }

  public static String replaceFirst(@NonNull String str,@NonNull String fs,@NonNull String rs,boolean isIgnoreCase){
    int ipos=0;
    if(isIgnoreCase){
      ipos=indexOf(str,fs,ipos);
      if(ipos>=0){
        str=str.substring(0,ipos)+rs+str.substring(ipos+fs.length());
      }
    }else{
      ipos=str.indexOf(fs,ipos);
      if(ipos>=0){
        str=str.substring(0,ipos)+rs+str.substring(ipos+fs.length());
      }
    }
    return str;
  }

  public static @NonNull String replaceAll(@NonNull String str,@NonNull String fs,@NonNull String rs,boolean isIgnoreCase){
    int ipos=0;
    StringBuffer sb=new StringBuffer(str);
    if(isIgnoreCase){
      while(ipos>=0){
        ipos=indexOf(sb.toString(),fs,ipos);
        if(ipos>=0){
          sb=sb.replace(ipos,ipos+fs.length(),rs);
          ipos+=rs.length();
        }
      }
    }else{
      while(ipos>=0){
        ipos=sb.indexOf(fs,ipos);
        if(ipos>=0){
          // sb=sb.substring(0,ipos)+rs+sb.substring(ipos+fs.length());
          sb=sb.replace(ipos,ipos+fs.length(),rs);
          ipos+=rs.length();
        }
      }
    }
    return sb.toString();
  }

  public static @NonNull String replaceAll(@NonNull String str,@NonNull String fs,@NonNull String rs,boolean isIgnoreCase,boolean isCycle){
    if(isCycle){
      while(existOf(str,fs)){
        str=replaceAll(str,fs,rs,isIgnoreCase);
      }
      return str;
    }else
      return replaceAll(str,fs,rs,isIgnoreCase);
  }

  public static @NonNull String insertZero(@NonNull String value,int outputlen){
    return padLeft(value,outputlen,'0');
  }

  public static @NonNull String getRepeatString(@NonNull String value,int times){
    StringBuffer result=new StringBuffer();
    for(int i=0;i<times;i++)
      result.append(value);
    return result.toString();
  }

  public static String getRepeatString(@NonNull String value,int times,char delimiter){
    CharAppender ca=new CharAppender(delimiter);
    for(int i=0;i<times;i++){
      ca.append(value);
    }
    return ca.toString();
  }

  public static String changeDateTimeFormat(@NonNull String value){
    value=replaceAll(value,"/","-",false).trim();
    if((value.length()<8||value==null||value.length()>19)||value.length()==8&&value.indexOf("-")>=0){
      throw new SdkRuntimeException("日期:"+value+"格式有误,正确的格式:2004-09-22 00:00:00"+CRLF);
      // return "1900-01-01 00:00:00";
    }
    String result;
    if(value.length()==8)
      value=value.substring(0,4)+"-"+value.substring(4,6)+"-"+value.substring(6,8)+" 00:00:00";
    if(value.length()==19)
      return value;
    int ipos=0;
    String year=null;
    String month=null;
    String date=null;
    String hour=null;
    String minute=null;
    String second=null;
    try{
      year=value.substring(0,value.indexOf("-"));
      value=value.substring(value.indexOf("-")+1,value.length());
      month=insertZero(value.substring(0,value.indexOf("-")),2);
      value=value.substring(value.indexOf("-")+1,value.length());
      while(getSubStringCount(value,"  ")>0)
        value=replaceAll(value,"  "," ",false);
      if(value.indexOf(" ")<0)
        value=value.concat(" ");
      date=insertZero(value.substring(0,value.indexOf(" ")),2);
      value=value.substring(value.indexOf(" ")+1,value.length());
      ipos=value.indexOf(":");
      if(ipos>=0)
        hour=insertZero(value.substring(0,value.indexOf(":")),2);
      else
        hour="00";
      value=value.substring(value.indexOf(":")+1,value.length());
      ipos=value.indexOf(":");
      if(ipos>=0)
        minute=insertZero(value.substring(0,value.indexOf(":")),2);
      else if(value.length()>0)
        minute=insertZero(value.trim(),2);
      else
        minute="00";

      value=value.substring(value.indexOf(":")+1,value.length());
      ipos=value.indexOf(" ");
      if(ipos>=0)
        second=insertZero(value.trim(),2);
      else
        second="00";
      result=year.concat("-")+month.concat("-")+date.concat(" ")+hour.concat(":")+minute.concat(":")+second;
    }catch(Exception ex){
      throw new SdkRuntimeException("日期格式有误".concat(ex.toString()+CRLF));
    }
    return result;
  }

  public static @NonNull String ConvertUTF8(@NonNull String s){
    StringBuffer sb=new StringBuffer();
    for(int i=0;i<s.length();i++){
      char c=s.charAt(i);
      if(c>=0&&c<=255){
        sb.append(c);
      }else{
        byte[] b;
        try{
          b=Character.toString(c).getBytes("utf-8");
        }catch(Exception ex){
          ex.printStackTrace();
          b=new byte[0];
        }
        for(int j=0;j<b.length;j++){
          int k=b[j];
          if(k<0)
            k+=256;
          sb.append("%"+Integer.toHexString(k).toUpperCase());
        }
      }
    }
    return sb.toString();
  }

  public static @NonNull String inverse(@NonNull String value){
    char[] tmp=value.toCharArray();
    char[] result=new char[tmp.length];
    for(int i=0;i<tmp.length;i++){
      result[i]=tmp[tmp.length-i-1];
    }
    return new String(result);
  }

  /**英文半角符号转换为中文全角符号*/
  public static String convertChinesePunctuation(@NonNull String value){
    char[] chararr=value.toCharArray();
    for(int i=0;i<chararr.length;i++){
      chararr[i]=convertChinesePunctuation(chararr[i]);
    }
    return new String(chararr);
  }

  /**英文半角符号转换为中文全角符号*/
  public static char convertChinesePunctuation(char character){
    char result=character;
    char[] punctuations=new char[]{',','，','|','｜','<','〈','>','〉','\"','＂','$','＄','&','＆',':','：'};
    for(int i=0;i<punctuations.length;){
      if(punctuations[i]==character){
        result=punctuations[i+1];
        break;
      }
      i+=2;
    }
    return result;
  }

  public static boolean isEmpty(String value){
    return EasyObj.isBlank(value);
  }

  public static boolean isBlank(String value){
    return EasyObj.isBlank(value);
  }

  public static boolean isValid(String value){
    return value!=null&&value.length()>0;
  }

  public static int getISN(char c,String charsetName){
    int value=c;
    byte[] bs=null;
    if(charsetName.length()==0)
      charsetName="gb2312";
    try{
      bs=String.valueOf(c).getBytes(charsetName);
      if(bs.length>=2){ // 长度>=2就有可能是中文
        int b1=bs[0]&0xff;
        int b2=bs[1]&0xff;
        value=(b1<<8)|b2;
      }
      return value;
    }catch(Exception e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static @NonNull String inputStreamConvert(@NonNull InputStream is) throws Exception{
    int bufferSize=1024;
    byte buff[]=new byte[bufferSize];
    OutputStream xOutputStream=new ByteArrayOutputStream(bufferSize);
    int readSize;
    StringBuffer result=new StringBuffer();
    while((readSize=is.read(buff))!=-1){
      xOutputStream.write(buff,0,readSize);
      result.append(xOutputStream.toString());
    }
    return result.toString();
  }

  public static String getResourceAsString(@NonNull String fileName){
    return EasyStr.isToStr(EasyStr.class.getClassLoader().getResourceAsStream(fileName));
    // getClass().getClassLoader()

  }

  public static String left(@NonNull String value,int len){
    if(len>value.length())
      len=value.length();
    else if(len<0)
      len=0;
    return value.substring(0,len);
  }

  public static String right(@NonNull String value,int len){
    if(isBlank(value))
      return value;
    if(len>value.length())
      len=value.length();
    else if(len<0)
      len=0;
    return value.substring(value.length()-len,value.length());
  }

  public static @NonNull String mid(@NonNull String value,int start,int len){
    if(start<0)
      start=0;
    if(len<=0||len+start>value.length())
      len=value.length()-start;
    return value.substring(start,start+len);
  }

  /**
   * 返回非null的字符串
   */
  public static @NonNull String getText(String value){
    return ifNull(value,"");
  }

  /**
   * 返回非null的字符串
   */
  public static @NonNull String getText(String value,String defVal){
    return ifNull(value,defVal);
  }

  /**
   * 返回非null的字符串
   */
  public static @NonNull String getText(Object obj){
    return getText(esdk.str.valueOf(obj));
  }

  /**
   * 如果为空，返回默认值
   */
  public static @NonNull String ifBlank(String value,@NonNull String defaultValue){
    if(value!=null&&value.length()>0)
      return value;
    return defaultValue;
  }

  /**
   * 如果为null，返回默认值
   */
  public static @NonNull String ifNull(String value,String defaultValue){
    if(value!=null)
      return value;
    return defaultValue;
  }

  public static String[] sort(@NonNull String[] obj){
    return esdk.array.sort(obj);
  }

  public static String[] SortArray(@NonNull String[] obj){
    return esdk.array.sort(obj);
  }

  public static boolean equals(String[] a,String[] b){
    boolean result=true;
    if(a==b)
      return true;
    a=SortArray(a);
    b=SortArray(b);
    if(a.length==b.length){
      for(int i=0;i<b.length;i++){
        if(!a[i].equals(b[i])){
          result=false;
          break;
        }
      }
    }else{
      result=false;
    }
    return result;
  }

  public static String[] split(String s,String regex[],int inx){
    if(isBlank(s))
      return new String[0];
    String arr[];
    for(int i=0;i<regex.length;i++){
      arr=s.split(regex[i]);
      if(arr.length!=1){
        if(inx==-1){
          return arr;
        }else if(inx>=0&&inx<arr.length){
          return new String[]{arr[inx]};
        }
      }
    }
    return new String[0];
  }

  /**
   * 只匹配第一个分隔符，不管后面是否已匹配，为的是提升性能
   */
  public static String[] splitFirst(String str,String sep){
    int index=str.indexOf(sep);
    if(index<0)
      return new String[]{str};
    else{
      String[] result=new String[2];
      result[0]=str.substring(0,index);
      result[1]=str.substring(index+1);
      return result;
    }
  }

  /**
   * 适用于properties的多种分隔符（eg:逗号、分号、回车、竖号、制表符、空格等）同时共存的值进行分割
   */
  public static @NonNull String[] splits(String values){
    if(isBlank(values))
      return new String[0];
    String[] result=values.split("[,， ;；	\\s\\r\\n\\t+\\|]+");
    return result;
  }

  /**
   * 按词分隔符进行分隔
   */
  public static String[] splitWords(String words){
    if(isBlank(words))
      return new String[0];
    Pattern p=Pattern.compile("([\\u4E00-\\u9FA5\\w]+)",Pattern.CASE_INSENSITIVE|Pattern.DOTALL);
    Matcher m=p.matcher(words);
    ArrayList<String> list=new ArrayList();
    while(m.find()){
      list.add(m.group(1));
    }
    String[] result=list.toArray(new String[0]);
    esdk.sout(esdk.str.join(result));
    return result;
  }

  /**
   * 适用于多种分隔符（eg:逗号、分号、回车、竖号、制表符等）按优先级进行分割
   */
  public static @NonNull String[] split(String values){
    if(isBlank(values))
      return new String[0];
    else if(values.indexOf('\t')>=0)
      return values.split("\t");
    else if(values.contains(CRLF))
      return values.split(CRLF);
    else if(values.contains("\n"))
      return values.split("\n");
    else if(values.contains(";"))
      return values.split(";");
    else if(values.contains(DefaultSeperator))
      return values.split(DefaultSeperator);
    else if(values.contains("|"))
      return values.split("\\|");
    else if(values.contains("；"))
      return values.split("；");
    else if(values.contains("，"))
      return values.split("，");
    else if(values.contains(" "))
      return values.split(" +");
    else if(values.contains("	"))
      return values.split("	+");
    else
      return new String[]{values};
  }

  public static String[] splitWithTrim(String values){
    return split(getText(values).replaceAll(" ",""));
  }

  public static String[] split(String s,String delimiter){
    if(isBlank(s))
      return new String[0];
    else
      return s.split(delimiter);
  }


  /*
   * public static boolean isIncludeChinese(String value) { if(value==null)
   * return false; boolean result=false; for(int i=0,n=value.length();i<n;i++){
   * if(isChinese(value.charAt(i))) { result=true; break; } } return result; }
   */

  /**
   * 与split如果最后是分隔符，则返回的数组会少一个，seperate会把这个空字符补上去
   */
  public static String[] seperate(String s,String delimiter){
    String[] result=split(s,delimiter);
    if(s.matches(".*?"+delimiter+"$")){
      result=esdk.str.append(result,"");
    }
    return result;
  }

  public static boolean isContainChinese(String value){
    return isIncludeChinese(value);
  }

  public static boolean isIncludeChinese(String value){
    if(value==null)
      return false;
    String regExp="[\u4E00-\u9FA5]";
    Pattern p=Pattern.compile(regExp);
    Matcher m=p.matcher(value);
    return m.find();
  }

  public static boolean isChinese(char value){
    byte numeric=((byte)(value>>8));
    boolean result=numeric!=0;
    return result;
  }

  public static boolean isEnglish(char value){
    return (value>='A'&&value<='Z')||(value>='a'&&value<='z');
  }

  public static boolean isCapital(char value){
    return value>='A'&&value<='Z';
  }

  public static boolean isNumeric(char value){
    return (value>='0'&&value<='9');
  }

  public static boolean equals(String str1,String str2){
    return str1==null?str2==null:str1.equals(str2);
  }

  public static boolean equals(String o1,String o2,boolean isIgnoreCase){
    boolean result=false;
    if(isIgnoreCase){
      if(o1==o2)
        result=true;
      else if(o1==null||o2==null)
        result=false;
      else if(o1!=null)
        result=o1.equalsIgnoreCase(o2);
    }else
      result=ObjectUtil.equal(o1,o2);
    return result;
  }

  public static int indexOf(@NonNull String[] array,@NonNull String obj){
    for(int i=0;array!=null&&i<array.length;i++){
      if(equals(array[i],obj,true))
        return i;
    }
    return -1;
  }

  public static boolean existOf(@NonNull String[] array,String obj){
    return indexOf(array,obj)>=0;
  }

  /**
   * 等同于existAnd
   */
  public static boolean existAnd(@NonNull String[] array,String[] subArray){
    for(int i=0, len=subArray.length;i<len;i++){
      if(!existOf(array,subArray[i])){
        return false;
      }
    }
    return true;
  }

  public static @NonNull String getStringNoNull(Object obj){
    if(obj==null)
      return "";
    return valueOf(obj);
  }

  public static @NonNull String getStringWhenBlank(String str,String defaultValue){
    return !isBlank(str)?str:defaultValue;
  }

  /**
   * key忽略大小写写入value
   */
  public static <R> R putIgnoreCase(Map<String,R> map,String key,R value){
    key=findKeyIgnoreCase(map,key);
    return map.put(key,value);
  }

  /**
   * 安全获取value
   */
  public static <R> R get(Map<?,R> map,Object key){
    if(key==null||map==null)
      return null;
    return map.get(key);
  }

  /**
   * 安全获取value
   */
  public static <R> R get(Map map,Object key,R def){
    if(key==null||map==null)
      return null;
    return (R)map.getOrDefault(key,def);
  }

  /**
   * key忽略大小写获取value
   */
  public static Object getIgnoreCase(@NonNull Map map,@NonNull Object key){
    Object result=get(map,key);
    if(result==null&&(key instanceof CharSequence)){
      for(Iterator iter=map.keySet().iterator();iter.hasNext();){
        String realKey=EasyStr.valueOf(iter.next());
        if(equals(realKey,key.toString(),true))
          return map.get(realKey);
      }
    }
    return result;
  }

  /**
   * 查找key不分大小写
   */
  public static String findKeyIgnoreCase(@NonNull Map<String,?> map,@NonNull String key){
    boolean result=map.containsKey(key);
    if(result)
      return key;
    if(!result&&(key instanceof CharSequence)){
      for(Iterator iter=map.keySet().iterator();iter.hasNext();){
        String realKey=iter.next().toString();
        if(equals(realKey,key.toString(),true))
          return realKey;
      }
    }
    return key;
  }

  /**
   * 避免NullPointException
   */
  public static boolean contains(String str,String key){
    return str!=null&&key!=null&&str.contains(key);
  }

  public static boolean contains(@NonNull Collection map,Object key){
    boolean result=map.contains(key);
    if(!result&&(key instanceof CharSequence)){
      for(Iterator iter=map.iterator();iter.hasNext();){
        String realKey=iter.next().toString();
        if(equals(realKey,key.toString(),true)){
          result=true;
          break;
        }
      }
    }
    return result;
  }

  public static boolean wildCardMatch(@NonNull String str,@NonNull String find,boolean isIgnoreCase){
    if(isIgnoreCase)
      return TestHelper.wildCardMatch(str.toLowerCase(),find.toLowerCase());
    else
      return TestHelper.wildCardMatch(str,find);
  }

  public static boolean wildCardMatch(String str,String find){
    return TestHelper.wildCardMatch(str,find);
  }

  public static String increase(@NonNull String numStr,int addend){
    String result=numStr;
    if(addend==0)
      return result;
    for(int i=numStr.length()-1;i>=0;i--){
      char c=numStr.charAt(i);
      if(!(c>='0'&&c<='9')){
        String str=numStr.substring(0,i+1);
        String number=numStr.substring(i+1);
        if(number.length()==0){
          if(addend>0)
            result=str+addend;
        }else{
          result=str+(Integer.valueOf(number).intValue()+addend);
        }
        break;
      }
    }
    return result;
  }

  /**
   * 格式化字符串，参数格式用${key}表示
   *
   * @maps 参数集合，把多个map先合并成一个
   */
  public static String format(String template,boolean ignoreNotFound,Map[] maps){
    Map map=esdk.map.merge(true,null,maps);
    return format(template,ignoreNotFound,map);
  }

  public static String format(String template,Map map){
    return format(template,false,map);
  }

  public static String deepFormat(@NonNull String template,boolean ignoreNotFound,Map map){
    String result=format(template,ignoreNotFound,map);
    if(result.contains("${"))
      return deepFormat(result,ignoreNotFound,map);
    else
      return result;
  }

  /**
   * 格式化字符串，参数格式用${key}表示
   *
   * @ignoreNotFound 为true时null自动改为""
   */
  public static String format(@NonNull String template,boolean ignoreNotFound,Map map){
    Pattern p=Pattern.compile("(\\$?\\{)([\\w\\.\\u4E00-\\u9FA5]+)(\\})"); // 匹配中文、英文、数字
    Matcher m=p.matcher(template);
    StringBuffer result=new StringBuffer();
    while(m.find()){
      String value=esdk.str.valueOf(map.get(m.group(2)));
      esdk.tool.throw4Error(value==null&&!ignoreNotFound,"没有找到字符串模板参数：'"+m.group(2)+"'");
      value=getText(value);
      value=esdk.regex.quota(value);
      m.appendReplacement(result,value);
    }
    result=m.appendTail(result);
    return result.toString();
  }

  /**
   * 传参数的方式格式化字符串，参数模板用{}或{0...n}的方式表示，注意不是${}
   * 注意：如果模板没有{}或{0}，则直接返回模板内容，而不是拼接字符中。
   */
  public static String format(@NonNull String template,String... params){
    String result=template;
    if(esdk.regex.find(template,"\\{\\d{0,2}\\}")&&params.length>0){
      for(int i=0;i<params.length;i++){
        int start=result.indexOf("{}");
        String param=params[i]+"";
        if(start>=0)
          result=result.substring(0,start).concat(param).concat(result.substring(start+2));
        else
          result=result.replace("{"+i+"}",params[i]+"");
      }
    }
    return result;
  }

  public static String format(@NonNull String template,Object... params){
    if(esdk.regex.find(template,"\\{\\d{0,2}\\}")&&params.length>0){
      int start=0;
      for(int i=0;i<params.length;i++){
        start=template.indexOf("{}",start);
        String param=ifNull(valueOf(params[i]),"null");
        if(start>=0){
          template=template.substring(0,start).concat(param).concat(template.substring(start+2));
          start+=param.length();
        }else{
          template=template.replace("{"+i+"}",param);
        }
      }
    }
    return template;
  }

  public static String toUnderlineCase(@NonNull String javaBeanName){
    return StrUtil.toUnderlineCase(javaBeanName);
  }

  /**
   * 驼峰表示
   */
  public static String toCamelCase(@NonNull String columnName,boolean isCaptial){
    if(columnName.matches("[A-Z]+"))
      columnName=columnName.toLowerCase();
    String result=StrUtil.toCamelCase(columnName);
    if(isCaptial){
      result=StrUtil.upperFirst(result);
    }else{
      result=StrUtil.lowerFirst(result);
    }
    return result;
  }

  /**
   * 驼峰表示，首字母小写
   */
  public static String toCamelCase(@NonNull String columnName){
    return toCamelCase(columnName,false);
  }

  /**
   * 首字母大写
   */
  public static String upperFirst(@NonNull String columnName){
    return StrUtil.upperFirst(columnName);
  }

  /**
   * 首字母小写
   */
  public static String lowerFirst(@NonNull String columnName){
    return StrUtil.lowerFirst(columnName);
  }

  /**
   * 把数组连接为字符串
   */
  public static String concat(Object... array){
    StringBuilder result=new StringBuilder();
    if(array!=null){
      for(int i=0;i<array.length;i++){
        result.append(array[i]==null?"":array[i]);
      }
    }
    return result.toString();
  }

  /**
   * 拼接全称
   *
   * @param parent 如果为空，只返回当前名称
   */
  public static String getFullName(String parent,String separator,String name){
    if(isValid(parent))
      return parent+separator+name;
    else
      return name;
  }

  /**
   * 去掉多个字符串中重复的内容，合并成一个字符串
   */
  public static @NonNull String unistr(char delimiter,String... strings){
    String[][] arrarr=new String[strings.length][];
    for(int i=0;i<strings.length;i++){
      arrarr[i]=esdk.str.split(strings[i],delimiter+"");
    }
    String[] result=distinct(arrarr);
    return join(delimiter,result);
  }

  public static <T> String[] unique(@NonNull T... array){
    return distinct(array);
  }

  public static <T> String[] distinct(@NonNull T... array){
    return (String[])EasyArray.unique((Class)String.class,array);
  }

  public static String[] remove(@NonNull String[] arr,String... removeItems){
    return EasyArray.remove(arr,removeItems);
  }

  public static @NonNull String remove(@NonNull String arr,String subarr){
    return join(',',remove(split(arr),split(subarr)));
  }

  public static String remove(@NonNull String arr,String subarr,char delimiter){
    return join(delimiter,remove(split(arr),split(subarr)));
  }

  public static String[] append(String[] arr,String... appendArr){
    return esdk.array.append(arr,appendArr);
  }

  /**
   * param[objects] should be String[] or String
   */
  public static <T> String[] toArray(T... strOrArr){
    return EasyArray.concat(String.class,strOrArr);
  }

  public static @NonNull String join(Object[] arr){
    return join(',',arr);
  }

  /**
   * 把数组连接成字符串
   */
  public static @NonNull String join(char delimiter,@NonNull Object[] arr){
    StringBuilder result=new StringBuilder();
    for(int i=0;i<arr.length;i++){
      result.append((i>0?delimiter:"")+valueOf(arr[i]));
    }
    return result.toString();
  }

  /**
   * 把数组连接成字符串
   */
  public static @NonNull String join(char delimiter,String... arr){
    StringBuilder result=new StringBuilder();
    for(int i=0;i<arr.length;i++){
      result.append((i>0?delimiter:"")+arr[i]);
    }
    return result.toString();
  }

  /**
   * 把数组连接成字符串，忽略空字符串
   */
  public static @NonNull String join(boolean ignoreEmpty,char delimiter,String... arr){
    StringBuilder result=new StringBuilder();
    for(int i=0;i<arr.length;i++){
      if(ignoreEmpty){
        if(isValid(arr[i])){
          result.append((result.length()>0?delimiter:"")+arr[i]);
        }
      }else{
        result.append((i>0?delimiter:"")+arr[i]);
      }
    }
    return result.toString();
  }

  public static @NonNull String join(@NonNull String[] arr,char delimiter){
    return join(delimiter,arr);
  }

  public static @NonNull String join(@NonNull Object[] arr,char delimiter){
    return join(delimiter,arr);
  }

  /**
   * 把数组连接成字符串
   */
  public static @NonNull String join(@NonNull List<?> list,char delimiter){
    StringBuilder result=new StringBuilder();
    for(int i=0;i<list.size();i++){
      if(i>0)
        result.append(delimiter+valueOf(list.get(i)));
      else
        result.append(valueOf(list.get(i)));
    }
    return result.toString();
  }

  public static @NonNull String join(@NonNull String delimiter,String... elements){
    return String.join(delimiter,elements);
  }

  public static @NonNull String arrToStr(Object[] array,char delimer){
    CharAppender result=new CharAppender(delimer);
    for(int i=0;i<array.length;i++){
      if(array[i]==null)
        array[i]="null";
      result.add(array[i].toString());
    }
    return result.toString();
  }

  /**
   * Produce a string in double quotes with backslash sequences in all the right
   * places. A backslash will be inserted within </, allowing JSON text to be
   * delivered in HTML. In JSON text, a string cannot contain a control
   * character or an unescaped quote or backslash.<br>
   * <strong>CAUTION:</strong> if <code>string</code> represents a javascript
   * function, translation of characters will not take place. This will produce
   * a non-conformant JSON text.
   *
   * @param string A String
   * @return A String correctly formatted for insertion in a JSON text.
   */
  public static @NonNull String quote(String string){
    if(string==null||string.length()==0){
      return "\"\"";
    }
    char b;
    char c=0;
    int i;
    int len=string.length();
    StringBuilder sb=new StringBuilder(len*2);
    String t;
    char[] chars=string.toCharArray();
    char[] buffer=new char[1030];
    int bufferIndex=0;
    sb.append('"');
    for(i=0;i<len;i+=1){
      if(bufferIndex>1024){
        sb.append(buffer,0,bufferIndex);
        bufferIndex=0;
      }
      b=c;
      c=chars[i];
      switch(c){
        case '\\':
        case '"':
          buffer[bufferIndex++]='\\';
          buffer[bufferIndex++]=c;
          break;
        case '/':
          if(b=='<'){
            buffer[bufferIndex++]='\\';
          }
          buffer[bufferIndex++]=c;
          break;
        default:
          if(c<' '){
            switch(c){
              case '\b':
                buffer[bufferIndex++]='\\';
                buffer[bufferIndex++]='b';
                break;
              case '\t':
                buffer[bufferIndex++]='\\';
                buffer[bufferIndex++]='t';
                break;
              case '\n':
                buffer[bufferIndex++]='\\';
                buffer[bufferIndex++]='n';
                break;
              case '\f':
                buffer[bufferIndex++]='\\';
                buffer[bufferIndex++]='f';
                break;
              case '\r':
                buffer[bufferIndex++]='\\';
                buffer[bufferIndex++]='r';
                break;
              default:
                t="000"+Integer.toHexString(c);
                int tLength=t.length();
                buffer[bufferIndex++]='\\';
                buffer[bufferIndex++]='u';
                buffer[bufferIndex++]=t.charAt(tLength-4);
                buffer[bufferIndex++]=t.charAt(tLength-3);
                buffer[bufferIndex++]=t.charAt(tLength-2);
                buffer[bufferIndex++]=t.charAt(tLength-1);
            }
          }else{
            buffer[bufferIndex++]=c;
          }
      }
    }
    sb.append(buffer,0,bufferIndex);
    sb.append('"');
    return sb.toString();
  }

  public static @NonNull String serial(String filename,int serialLength){
    String newFileName=esdk.regex.replaceFirst(filename,"(.*?)(\\d*)$",matcher->{
      int serial=esdk.math.toInt(matcher.group(2))+1;
      return matcher.group(1)+padLeft(serial+"",serialLength,'0');
    });
    return newFileName;
  }

  /**
   * get the next number from string
   */
  public static @NonNull String serial(String value){
    if(isBlank(value))
      return "1";
    Pattern p=Pattern.compile("(.*?)(\\d*)");
    Matcher m=p.matcher(value);
    if(m.matches()){
      value=m.group(1)+(m.group(2).length()==0?1:insertZero(String.valueOf((Integer.valueOf(m.group(2))+1)),m.group(2).length()));
    }
    return value;
  }

  public static String trim(CharSequence value){
    if(value==null) return null;
    return delInvisibleChar(value).trim();
  }

  /**
   * 删除不可见字符，包括控制字符、删除符、零宽字符、窄空格、细空格等
   */
  public static @NonNull String delInvisibleChar(CharSequence value){
    return value.toString().replaceAll("[\\p{Cntrl}&&[^\\t\\n\\r]|[\u007F\u200B\u2009\u200A]]+","");
  }

  public static String valueOf(Object obj){
    return valueOf(obj,null);
  }

  public static String valueOf(Object obj,String dateFormat){
    return valueOf(obj,dateFormat,null);
  }

  public static String valueOf(Object obj,String dateFormat,String defultValue){
    if(obj==null){
        return defultValue;
    }else if(obj instanceof CharSequence)
      return obj.toString();
    else if(obj instanceof Number)
      return obj.toString();
    else if(obj instanceof java.sql.Timestamp&&dateFormat==null)
      return obj.toString();
    else if(obj instanceof Time){
      return obj.toString();
    }else if(obj instanceof Date){
      if(dateFormat!=null)
        return EasyTime.format((Date)obj,dateFormat);
      else
        return esdk.time.format((Date)obj);
    }else if(obj instanceof LocalDateTime localDateTime)
      return esdk.time.format(localDateTime);
    else if(obj instanceof LocalDate localdate)
      return localdate.toString();
    else if(obj instanceof Object[]){
      Object[] array=(Object[])obj;
      CharAppender ca=new CharAppender(',');
      for(int i=0;i<array.length;i++){
        ca.append(valueOf(array[i]));
      }
      return ca.toString();
    }else if(obj instanceof Map<?,?>){
      return esdk.map.mapToStr((Map)obj);
    }else if(obj instanceof Collection){
      CharAppender ca=new CharAppender(',');
      for(Iterator iter=((Collection)obj).iterator();iter.hasNext();){
        ca.append(valueOf(iter.next()));
      }
      return ca.toString();
    }else if(obj.getClass().getName().equals("oracle.sql.TIMESTAMP")){
      Timestamp timestamp=EasySql.getOracleTimestamp(obj);
      if(dateFormat==null)
        return timestamp.toString();
      else
        return EasyTime.format(timestamp,dateFormat);
    }else if(obj.getClass().getName().equals("oracle.sql.CLOB")){
      String clobstr=esdk.str.readerToStr((Reader)esdk.reflect.getMethodValue(obj,"characterStreamValue"));
      return clobstr;
    }else if(obj instanceof Throwable){
      String error=esdk.tool.getExceptionStackTrace((Throwable)obj);
      return error;
    }else
      return obj.toString();
  }

  public static String native2Ascii(@NonNull String str){
    char[] chars=str.toCharArray();
    StringBuilder sb=new StringBuilder();
    for(int i=0;i<chars.length;i++){
      sb.append(char2Ascii(chars[i]));
    }
    return sb.toString();
  }

  private static String char2Ascii(char c){
    if(c>255){
      StringBuilder sb=new StringBuilder();
      sb.append(PREFIX);
      int code=(c>>8);
      String tmp=Integer.toHexString(code);
      if(tmp.length()==1){
        sb.append("0");
      }
      sb.append(tmp);
      code=(c&0xFF);
      tmp=Integer.toHexString(code);
      if(tmp.length()==1){
        sb.append("0");
      }
      sb.append(tmp);
      return sb.toString();
    }else{
      return Character.toString(c);
    }
  }

  public static String ascii2Native(@NonNull String str){
    StringBuilder sb=new StringBuilder();
    int begin=0;
    int index=str.indexOf(PREFIX);
    while(index!=-1){
      sb.append(str.substring(begin,index));
      sb.append(ascii2Char(str.substring(index,index+6)));
      begin=index+6;
      index=str.indexOf(PREFIX,begin);
    }
    sb.append(str.substring(begin));
    return sb.toString();
  }

  private static char ascii2Char(@NonNull String str){
    if(str.length()!=6){
      throw new IllegalArgumentException("Ascii string of a native character must be 6 character.");
    }
    if(!PREFIX.equals(str.substring(0,2))){
      throw new IllegalArgumentException("Ascii string of a native character must start with \"\\u\".");
    }
    String tmp=str.substring(2,4);
    int code=Integer.parseInt(tmp,16)<<8;
    tmp=str.substring(4,6);
    code+=Integer.parseInt(tmp,16);
    return (char)code;
  }

  /* 如果为null或空都认为是false，只返回有值的字串 */
  public static String or(String... vars){
    for(int i=0;i<vars.length;i++){
      if(vars[i]!=null&&vars[i].length()>0)
        return vars[i];
    }
    return vars[vars.length-1];
  }

  public static String or(String p1,String p2){
    if(p1!=null&&!p1.equals(""))
      return p1;
    else if(p2!=null&&!p2.equals(""))
      return p2;
    else if(p1!=null)
      return p1;
    else
      return p2;
  }

  public static HashSet splitToSet(String str){
    return splitToSet(str,null,true);
  }

  public static HashSet splitToSet(String str,boolean isOrdered){
    return splitToSet(str,null,isOrdered);
  }

  public static HashSet splitToSet(@NonNull String str,@NonNull String separator,boolean isOrdered){
    HashSet result=isOrdered?new LinkedHashSet():new HashSet();
    if(str==null)
      return result;
    String[] array=separator==null?split(str):split(str,separator);
    for(String item: array){
      result.add(item);
    }
    return result;
  }

  public static String setToStr(@NonNull HashSet set){
    return setToStr(set,DefaultSeperator);
  }

  public static String setToStr(@NonNull HashSet set,@NonNull String separator){
    if(set==null)
      return null;
    else if(set.size()==0)
      return "";
    if(separator==null)
      separator=DefaultSeperator;
    String result="";
    for(Iterator iter=set.iterator();iter.hasNext();){
      result+=iter.next()+"";
      if(iter.hasNext())
        result+=separator;
    }
    return result;
  }

  public static String encodeUnicode(@NonNull String decodeStr){
    char[] myBuffer=decodeStr.toCharArray();

    StringBuffer sb=new StringBuffer();
    for(int i=0;i<decodeStr.length();i++){
      UnicodeBlock ub=UnicodeBlock.of(myBuffer[i]);
      if(ub==UnicodeBlock.BASIC_LATIN){
        // 英文及数字等
        sb.append(myBuffer[i]);
      }else if(ub==UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS){
        // 全角半角字符
        int j=(int)myBuffer[i]-65248;
        sb.append((char)j);
      }else{
        // 汉字
        short s=(short)myBuffer[i];
        String hexS=Integer.toHexString(s);
        String unicode="\\u"+hexS;
        sb.append(unicode.toLowerCase());
      }
    }
    return sb.toString();
  }

  /**
   * unicode 转换成 中文
   *
   * @param theString
   * @return
   * @author fanhui 2007-3-15
   */
  public static String decodeUnicode(@NonNull String theString){
    char aChar;
    int len=theString.length();
    StringBuffer outBuffer=new StringBuffer(len);
    for(int x=0;x<len;){
      aChar=theString.charAt(x++);
      if(aChar=='\\'){
        aChar=theString.charAt(x++);
        if(aChar=='u'){
          int value=0;
          for(int i=0;i<4;i++){
            aChar=theString.charAt(x++);
            switch(aChar){
              case '0':
              case '1':
              case '2':
              case '3':
              case '4':
              case '5':
              case '6':
              case '7':
              case '8':
              case '9':
                value=(value<<4)+aChar-'0';
                break;
              case 'a':
              case 'b':
              case 'c':
              case 'd':
              case 'e':
              case 'f':
                value=(value<<4)+10+aChar-'a';
                break;
              case 'A':
              case 'B':
              case 'C':
              case 'D':
              case 'E':
              case 'F':
                value=(value<<4)+10+aChar-'A';
                break;
              default:
                throw new IllegalArgumentException("Malformed encoding.");
            }
          }
          outBuffer.append((char)value);
        }else{
          if(aChar=='t'){
            aChar='\t';
          }else if(aChar=='r'){
            aChar='\r';
          }else if(aChar=='n'){
            aChar='\n';
          }else if(aChar=='f'){
            aChar='\f';
          }
          outBuffer.append(aChar);
        }
      }else{
        outBuffer.append(aChar);
      }
    }
    return outBuffer.toString();
  }

  public static byte[] getUTF8Bytes(@NonNull String s){
    try{
      return s.getBytes(Constant.UTF8);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static String getUTF8String(byte[] utf8bytes){
    try{
      return new String(utf8bytes,Constant.UTF8);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static String encoding(String v,String encodingFrom,String encodingTo){
    if(v==null||encodingFrom==null||encodingFrom==null)
      return v;
    try{
      return new String(v.getBytes(encodingFrom),encodingTo);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public static String encodeBySystem(String str){
    if(str==null)
      return null;
    if(!esdk.tool.getSystemEncoding().equals(Constant.UTF8)){
      try{
        return new String(str.getBytes(esdk.tool.getSystemEncoding()),Constant.UTF8);
      }catch(UnsupportedEncodingException e){
        throw esdk.tool.wrapThrowble(e);
      }
    }else
      return str;
  }

  public static String getUTF8StringFromGBKString(String gbkStr){
    try{
      return new String(getUTF8BytesFromGBKString(gbkStr),"UTF-8");
    }catch(UnsupportedEncodingException e){
      throw new InternalError();
    }
  }

  public static byte[] getUTF8BytesFromGBKString(String gbkStr){
    int n=gbkStr.length();
    byte[] utfBytes=new byte[3*n];
    int k=0;
    for(int i=0;i<n;i++){
      int m=gbkStr.charAt(i);
      if(m<128&&m>=0){
        utfBytes[k++]=(byte)m;
        continue;
      }
      utfBytes[k++]=(byte)(0xe0|(m>>12));
      utfBytes[k++]=(byte)(0x80|((m>>6)&0x3f));
      utfBytes[k++]=(byte)(0x80|(m&0x3f));
    }
    if(k<utfBytes.length){
      byte[] tmp=new byte[k];
      System.arraycopy(utfBytes,0,tmp,0,k);
      return tmp;
    }
    return utfBytes;
  }

  /**
   * 往右边填入指定长度的字符
   */
  public static String padRight(@NonNull String src,int len,char ch){
    int diff=len-src.length();
    if(diff<=0){
      return src;
    }
    char[] charr=new char[len];
    System.arraycopy(src.toCharArray(),0,charr,0,src.length());
    for(int i=src.length();i<len;i++){
      charr[i]=ch;
    }
    return new String(charr);
  }

  /**
   * 往左边填入指定长度的字符
   */
  public static String padLeft(@NonNull String src,int len,char ch){
    int diff=len-src.length();
    if(diff<=0){
      return src;
    }

    char[] charr=new char[len];
    System.arraycopy(src.toCharArray(),0,charr,diff,src.length());
    for(int i=0;i<diff;i++){
      charr[i]=ch;
    }
    return new String(charr);
  }

  public static String delBegin(@NonNull String str,@NonNull String prefix){
    String result=str.startsWith(prefix)?str.substring(prefix.length(),str.length()):str;
    return result;
  }

  public static String addBegin(@NonNull String str,@NonNull String prefix){
    String result=str.startsWith(prefix)?str:prefix+str;
    return result;
  }

  public static String delEnd(@NonNull String str,@NonNull String postfix){
    String result=str.endsWith(postfix)?str.substring(0,str.length()-postfix.length()):str;
    return result;
  }

  public static String addEnd(@NonNull String str,@NonNull String postfix){
    String result=str.endsWith(postfix)?str:str+postfix;
    return result;
  }

  /**
   * 方便阅读长数字，中间加上,号
   */
  public static String humanFormat(long val){
    return df.format(val);
  }

  /**
   * 方便阅读长数字，中间加上,号
   */
  public static String humanFormat(double val){
    return df.format(val);
  }

  public static void println(String template,Object... args){
    System.out.println(format(template,args));
  }

  /**
   * 删除重复的词语，如abab改为ab，但aa这种单字重复的不处理
   */
  public static String removeRepeat(@NonNull String s){
    String result=s, sub;
    do{
      sub=result;
      result=sub.replaceAll("([\\w,\u4E00-\u9FA5]{2,})\\1","$1");
    }while(!sub.equals(result));
    return result;
  }

  /**
   * 脱敏，只显示头尾，中间星号显示
   */
  public static String desensitize(String str){
    if(isBlank(str)||str.length()<3) return str;
    return str.charAt(0)+fillchar('*',str.length()-2)+str.charAt(str.length()-1);
  }

  /**
   * 忽略大小写匹配、支持换行
   */
  public static boolean matches(@NonNull String str,@NonNull String regex){
    return esdk.regex.matches(str,regex);
  }

  /**
   * 支持换行
   *
   * @param ignoreCase 是否忽略大小写
   */
  public static boolean matches(@NonNull String str,@NonNull String regex,boolean ignoreCase){
    return esdk.regex.matches(str,regex,ignoreCase);
  }

  public byte[] getBytes(@NonNull String s,@NonNull String encoding){
    try{
      return s.getBytes(encoding);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }

  public String getString(byte[] bytes,String encoding){
    if(bytes==null)
      return null;
    try{
      return new String(bytes,encoding);
    }catch(UnsupportedEncodingException e){
      throw esdk.tool.wrapThrowble(e);
    }
  }


}
